package com.fineaiops.gateway.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fineaiops.gateway.bean.LogEntity;
import com.fineaiops.gateway.bean.ServiceNode;
import com.fineaiops.gateway.service.ExpService;
import com.fineaiops.gateway.service.NodeService;
import com.fineaiops.gateway.util.ConstString;
import com.fineaiops.gateway.util.JsonBuilder;
import com.fineaiops.gateway.util.OkHttpUtil;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.*;

@RestController
@CrossOrigin(origins = {"*", "null"})
public class GatewayController {
    private final Logger LOG = LoggerFactory.getLogger(getClass());
    @Autowired
    private NodeService nodeService;
    @Autowired
    private ExpService elasticService;

    @RequestMapping(value = "/test")
    public String greeter() {
        return "hello, world";
    }

    @RequestMapping(value = "/user")
    public String getUserInfo(@RequestParam(name = "name", defaultValue = "xxx") String name) {
        return "{\"name\": \""+ name +"\"}";
    }

    @RequestMapping("/serviceTopo")
    public String getServiceTopo() {
        return new JsonBuilder()
                .put("nodes", nodeService.getAllServiceNode())
                .put("edges", nodeService.getServiceEdges())
                .build()
                .toJSONString();
    }
    public JSONObject getMetricsServiceTopo() {
        try {
            String url = "http://49.232.168.242:8080/getMetricsDiagnose";
            OkHttpUtil httpUtil = new OkHttpUtil();
            String metricTopo = httpUtil.post(url,"");
            JSONObject metricsResult = JSONObject.parseObject(metricTopo);
            if (metricsResult.getInteger("code") == 0) {
                return metricsResult.getJSONObject("data");
            }
            else return metricsResult;
        } catch (Exception e) {
            e.printStackTrace();
            return new JsonBuilder().put("code", 100).put("msg", e.toString()).build();
        }
    }

    // todo 加上时间
    @RequestMapping("/metricAndLogServiceTopo")
    public String getMetricAndLogServiceTopo() {
        return new JsonBuilder()
                .put("logs", JSONObject.parseObject(getServiceTopo()))
                .put("metrics", getMetricsServiceTopo())
                .build()
                .toJSONString();
    }

    @RequestMapping("/exception_categories")
    public String getExceptionList(
            @RequestParam(name = "services") String[] services,
            @RequestParam(name = "levels") long[] levels,
            @RequestParam(name = "status") long[] status,
            @RequestParam(name = "offset") int offset,
            @RequestParam(name = "limit") int limit,
            @RequestParam(name = "type") int type) {
        try {
            JSONArray array = new JSONArray();
            if (type == 0) {
                array = getLogExceptionList(services, levels, status, offset, limit);
            } else {
                array = getMetricsExceptionList(services, levels, status);
                if (array.size() < (offset + 1) * limit) {
                    array = JSONArray.parseArray(JSON.toJSONString(array.subList(offset * limit, array.size() - 1)));
                } else {
//                    array = JSONArray.parseArray(JSON.toJSONString(array.subList(offset * limit, (offset + 1) * limit - 1)));
                }
            }
            return array.toJSONString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "fail";
    }

    @RequestMapping("/exception_sum")
    public String getExceptionList(
            @RequestParam(name = "services") String[] services,
            @RequestParam(name = "levels") long[] levels,
            @RequestParam(name = "status") long[] status,
            @RequestParam(name = "type") int type) {
        try {
            int sum = 0;
            if (type == 0) {
                sum = elasticService.getExceptionCategories(services, levels, status).length;
            } else {
//                sum = getMetricsExceptionList(services, levels, status).size();
            }
            return new JsonBuilder().put("exp_sum", sum).build().toJSONString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "fail";
    }

    public JSONArray getMetricsExceptionList(
            String[] services,
            long[] levels,
            long[] status
    ) {
        try {
            String url = "http://49.232.168.242:8080/getAnomalousMetrics";
            OkHttpUtil httpUtil = new OkHttpUtil();
            String res = httpUtil.post(url, new JsonBuilder()
                    .put("service_ids", services)
                    .put("level", levels).build().toJSONString());
            LOG.info("metric serviceIDs: "+Arrays.toString(services));
            JSONObject metricsRes = JSONObject.parseObject(res);
            int resultcode = metricsRes.getInteger("code");
            if (resultcode == 0) {
                return metricsRes.getJSONObject("Result").getJSONArray("Exception_categories");
            }
        } catch (Exception e){
            LOG.error("metrics request failed: " + e);
        }
        LOG.error("metrics request failed");
        return new JSONArray();
    }

    public JSONArray getLogExceptionList (
             String[] services,
             long[] levels,
             long[] status,
             int offset,
             int limit) throws Exception{
            SearchHit[] hits = elasticService.getExceptionCategories(services, levels, status, offset, limit);
            JSONArray res = new JSONArray();
            Map<String, ServiceNode> serviceMap = new HashMap<>();
            for (SearchHit hit: hits) {
                Map<String, Object> sourceMap = hit.getSourceAsMap();
                String serviceID = (String)sourceMap.get("service_id");
                if (!serviceMap.containsKey(serviceID)) {
                    serviceMap.put(serviceID, nodeService.getServiceByID(serviceID));
                }
                res.add(new JsonBuilder()
                        .put("id", hit.getId())
                        .put("type", 0)
                        .put("level", sourceMap.get("level"))
                        .put("status", sourceMap.get("status"))
                        .put("content",((String)sourceMap.get("content")).length()>100
                                ? ((String)sourceMap.get("content")).substring(0,100)+"..."
                                :sourceMap.get("content"))
                        .put("service_name",serviceMap.get(serviceID).getName())
                        .put("service_id", serviceID)
                        .put("service_ip","192.168.0.1")
                        .put("first", sourceMap.get("first_report_time"))
                        .put("last", sourceMap.get("last_report_time"))
                        .build());
            }
            return res;
    }

    @RequestMapping("/metricsException")
    public String getMetricsDetail(@RequestParam("metrics_id") int mid,
                                   @RequestParam("service_id") String sid) {
        String url = "http://49.232.168.242:8080/getMetricDetail";
        OkHttpUtil httpUtil = new OkHttpUtil();
        String res = httpUtil.post(url, new JsonBuilder()
                .put("metrics_id", mid)
                .put("service_id", sid).build().toJSONString());
        return res;
    }
    @RequestMapping("/history_logs")
    public String getHistoryLogs(
            @RequestParam("category_id") String categoryID,
            @RequestParam("offset") int offset,
            @RequestParam("limit") int limit
            ) {
        try {
            List<LogEntity> logList = elasticService.getLogByCategory(categoryID, offset, limit, SortOrder.DESC);
            for (LogEntity log: logList) {
                if (log.getContent().length() > 200) {
                    log.setContent(log.getContent().substring(0,200)+"...");
                }
            }
            return new JsonBuilder()
                    .put("log_list", logList)
                    .build()
                    .toJSONString();
        }catch (Exception e) {
            return "fail";
        }
    }

    @RequestMapping("/exception")
    public String getExceptionDetail(@RequestParam("id") String id) {
        try {
            JSONObject map = JSONObject.parseObject(elasticService.getExceptionCateByID(id).getSourceAsString());
            String serviceID = (String)map.get("service_id");
            String serviceName = nodeService.getServiceByID(serviceID).getName();
            int size = elasticService.getLogSum(id);
            JSONObject res = new JsonBuilder()
                    .put("exception", new JsonBuilder()
                            .put("id", id)
                            .put("type", "日志")
                            .put("report_sum", size)
                            .put("host", "192.168.0.1")
                            .put("content", map.get("content"))
                            .put("service", serviceName)
                            .put("first_report_time", map.get("first_report_time"))
                            .put("last_report_time", map.get("last_report_time"))
                            .build())
                    .build();
            return JSON.toJSONString(res);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return "fail";
    }

    @RequestMapping("/relative_exception")
    public String getRelativeException(@RequestParam("id") String id) {
        try {
            Map<String,Object> map = elasticService.getExceptionCateByID(id).getSourceAsMap();
            String content = (String)map.get("content");
            String serviceID = (String)map.get("service_id");
            TreeMap<String, ArrayList<String>> relationMap = nodeService.getRelatedService(serviceID);
            ArrayList<JSONObject> res = new ArrayList<>();
            Map<String, ServiceNode> serviceMap = new HashMap<>();
            for (Map.Entry<String, ArrayList<String>> entry : relationMap.entrySet()) {
                SearchHit[] hits = elasticService.getRelativeExceptionByContent(content, entry.getValue());
                for (SearchHit hit : hits) {
                    Map<String, Object> sourceMap = hit.getSourceAsMap();
                    String text = (String)sourceMap.get("content");
                    text = text.length()>100?text.substring(0,100)+"...":text;
                    String relativeServiceID =(String)sourceMap.get("service_id");
                    if (!serviceMap.containsKey(relativeServiceID)) {
                        serviceMap.put(relativeServiceID, nodeService.getServiceByID(relativeServiceID));
                    }
                    res.add(new JsonBuilder()
                            .put("id", hit.getId())
                            .put("type", "日志")
                            .put("relation",entry.getKey())
                            .put("content", text)
                            .put("service", serviceMap.get(relativeServiceID).getName())
                            .build());
                }
            }
            return JSON.toJSONString(res);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "fail";
    }

    @RequestMapping("/model_list")
    public String getRelativeException() {
        return ConstString.mockModelList;
    }

    @RequestMapping("/submit_review")
    public String submitReview(@RequestParam("modelIDs") int[] ids,
                               @RequestParam("dataSet") int datasetID
                               ) {
        LOG.info("submit compare modelIDs: " + Arrays.toString(ids) + " dataSet: "+ datasetID);
        return ConstString.mockResultCode;
    }

    @RequestMapping("/history_review_reports")
    public String getReviewReports(@RequestParam("offset") int offset,
                                   @RequestParam("limit") String limit) {
        return ConstString.mockReviewReport;
    }

    @RequestMapping("/review_report")
    public String getReviewReports(@RequestParam("report_task_id") int id) {
        return ConstString.mockReportDetail;
    }

    @RequestMapping("/submit_compare")
    public String submitCompare(@RequestParam("base_task") int base_id,
                                @RequestParam("compare_task") int compare_id) {
        LOG.info("submit compare base: " + base_id + " cmpare: "+ compare_id);
        return ConstString.mockResultCode;
    }

    @RequestMapping("/history_diff_task")
    public String getDiffTasks(@RequestParam("offset") int offset,
                               @RequestParam("limit") String limit) {
        return ConstString.mockDiffTasks;
    }

    @RequestMapping("/diff_report")
    public String getDiffReportDetail(@RequestParam("diff_task_id") int id) {
        return ConstString.mockDiffReport;
    }

    @RequestMapping("/all_report_task_id")
    public String getAllReportTaskID() {
        return ConstString.mockAllReportIDs;
    }

    @RequestMapping("/log_trace")
    public String getLogTrace(@RequestParam("id") String id) {
        try {
            JSONArray res = new JSONArray();
            ArrayList<String> ids = nodeService.getLogTraceIDs(id);
            for(int i = ids.size()-1; i >= 0; i--) {
                JSONObject map = JSONObject.parseObject(elasticService.getExceptionCateByID(id).getSourceAsString());
                String text = (String) map.get("content");
                text = text.length()>100?text.substring(0,100)+"...":text;
                res.add(new JsonBuilder()
                        .put("id", ids.get(i))
                        .put("content", text)
//                        .put("service",nodeService.getServiceByID(ids.get(i)).getName())
                        .build());
            }
            return res.toJSONString();
        } catch (Exception e) {
            LOG.error(e.toString());
        }
        return "fail";
    }

    @RequestMapping("log_influence")
    public String getExpInfluence(@RequestParam("id") String id) {
        try {
            return nodeService.getLogInfluence(id).toJSONString();
        } catch (Exception e) {
            LOG.error(e.toString());
        }
        return "fail";
    }

}